////////////////////////////////////////////////////////////////////////////////
// Includes
////////////////////////////////////////////////////////////////////////////////

#include "SCSystem.hpp"

#include "OutOfBoundsException.hpp"
#include "SizeException.hpp"
#include <sstream>
#include <iostream>
////////////////////////////////////////////////////////////////////////////////
// Namespaces
////////////////////////////////////////////////////////////////////////////////

namespace SC {

////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////

using namespace std;
using namespace Exceptions;

SCSystem::SCSystem(const unsigned int word_size)
{
	this->schema1.resize(word_size);
	this->kernel.resize(word_size);
	this->schema2.resize(word_size);
}
	
SCSystem::SCSystem(const SCSystem & inst)
:	schema1(inst.schema1), kernel(inst.kernel), schema2(inst.schema2),
	super_systems(inst.super_systems), sub_systems(inst.sub_systems)
{}

SCSystem & SCSystem::operator = (const SCSystem & inst) {
	if (this != &inst) {
		this->schema1 = inst.schema1;
		this->kernel = inst.kernel;
		this->schema2 = inst.schema2;
		this->super_systems = inst.super_systems;
		this->sub_systems = inst.sub_systems;
	}
	return *this;
}

const bool SCSystem::Grab(SCSystem & sys) {
	// If the system to grab is the universe, it is refused
	if (sys.NbSuperSystems() == 0) return false;
	// If the system to grab is a parent, it is refused
	//if (this->HasSuperSystem(sys)) return false;
	// Check whether the current system already possesses "sys"
	if (this->HasSubSystem(sys)) return false;
	// Add the current system as new super system of "sys"
	sys.addSuperSystem(*this);
	// Add "sys" as new sub system of the current one
	this->addSubSystem(sys);
	// Grab ok
	return true;
}

const bool SCSystem::TakeFromSuperSystems(SCSystem & sys, const SCSystem & context) {
	// If the system to take over is the universe, it is refused
	if (sys.NbSuperSystems() == 0) return false;
	// If the system to take over is a parent, it is refused
	if (this->HasSuperSystem(sys)) return false;
	// Remove from "sys" all the super systems shared by "sys", the current
	// system and the context initiating this action
	unsigned int sup_size = sys.super_systems.size();
	vector<SCSystem *>::iterator itor = sys.super_systems.begin();
	while (itor != sys.super_systems.end()) {
		if ((find(this->super_systems.begin(), this->super_systems.end(), *itor) != this->super_systems.end()) &&
			(find(context.super_systems.begin(), context.super_systems.end(), *itor) != context.super_systems.end())) {
			(*itor)->sub_systems.erase(find((*itor)->sub_systems.begin(), (*itor)->sub_systems.end(), &sys));
			itor = sys.super_systems.erase(itor);
		}
		else ++itor;
	}
	// If no shared scope, forbidden action
	if (sys.super_systems.size() == sup_size) return false;
	// Grab "sys"
	sys.addSuperSystem(*this);
	this->addSubSystem(sys);
	// Take over ok
	return true;
}

const bool SCSystem::Release(SCSystem & sys) {
	// Check whether the current system really possesses "sys"
	if (!this->HasSubSystem(sys)) return false;
	// If "sys" has no other super system, it is refused
	if (sys.NbSuperSystems() == 1) return false;
	// Remove the current system as super system of "sys"
	sys.removeSuperSystem(*this);
	// Remove "sys" as sub system of the current one
	this->removeSubSystem(sys);
	// Release ok
	return true;
}

const bool SCSystem::ReleaseToSuperSystems(SCSystem & sys) {
	// Check whether the current system really possesses "sys"
	if (!this->HasSubSystem(sys)) return false;
	// If the current system has no parent (universe), it is refused
	if (this->NbSuperSystems() == 0) return false;
	// Release the system
	sys.removeSuperSystem(*this);
	this->removeSubSystem(sys);
	// All the super systems of the current one become super systems of "sys"
	for (unsigned int i=0; i<this->super_systems.size(); ++i) {
		sys.addSuperSystem(*this->super_systems[i]);
		this->super_systems[i]->addSubSystem(sys);
	}
	// Release to parents ok
	return true;
}

const string SCSystem::GetSchema1(const unsigned int pos, const unsigned int lgh) const {
	return this->schema1.substr(pos,(lgh!=0)?lgh:this->schema1.size()-pos);
}
		
void SCSystem::ReplaceSchema1(const string & str, const unsigned int pos, const unsigned int lgh) {
	if (((lgh != 0) && (str.size() != lgh)) || (str.size() > this->schema1.size()-pos)) {
		stringstream ss;
		ss << "Schema 1 part replacement string " << str << " of size " << str.size() << " ";
		if (pos + str.size() > this->schema1.size())
			ss << "placed at position " << pos << " goes beyond schema length of " << schema1.size() << " and would require a minimum length of " << (pos+str.size());
		else
			ss << "does not have the required size " << ((lgh!=0)?lgh:this->schema1.size()-pos) << " to fit at position " << pos;
		throw SizeException(ss.str());
	}
	this->schema1.replace(pos,(lgh!=0)?lgh:this->schema1.size()-pos,str);
}

const string SCSystem::GetKernel(const unsigned int pos, const unsigned int lgh) const {
	return this->kernel.substr(pos,(lgh!=0)?lgh:this->kernel.size()-pos);
}
		
void SCSystem::ReplaceKernel(const string & str, const unsigned int pos, const unsigned int lgh) {
	if (((lgh != 0) && (str.size() != lgh)) || (str.size() > this->kernel.size()-pos)) {
		stringstream ss;
		ss << "Kernel part replacement string " << str << " of size " << str.size() << " ";
		if (pos + str.size() > this->kernel.size())
			ss << "placed at position " << pos << " goes beyond kernel length of " << kernel.size() << " and would require a minimum length of " << (pos+str.size());
		else
			ss << "does not have the required size " << ((lgh!=0)?lgh:this->kernel.size()-pos) << " to fit at position " << pos;
		throw SizeException(ss.str());
	}
	this->kernel.replace(pos,(lgh!=0)?lgh:this->kernel.size()-pos,str);
}

const string SCSystem::GetSchema2(const unsigned int pos, const unsigned int lgh) const {
	return this->schema2.substr(pos,(lgh!=0)?lgh:this->schema2.size()-pos);
}
		
void SCSystem::ReplaceSchema2(const string & str, const unsigned int pos, const unsigned int lgh) {
	if (((lgh != 0) && (str.size() != lgh)) || (str.size() > this->schema2.size()-pos)) {
		stringstream ss;
		ss << "Schema 2 part replacement string " << str << " of size " << str.size() << " ";
		if (pos + str.size() > this->schema2.size())
			ss << "placed at position " << pos << " goes beyond schema length of " << schema2.size() << " and would require a minimum length of " << (pos+str.size());
		else
			ss << "does not have the required size " << ((lgh!=0)?lgh:this->schema2.size()-pos) << " to fit at position " << pos;
		throw SizeException(ss.str());
	}
	this->schema2.replace(pos,(lgh!=0)?lgh:this->schema2.size()-pos,str);
}	

/*
SCSystem & SCSystem::SuperSystem(const unsigned int i) {
	if (i >= this->superSystems.size()) {
		stringstream ss; ss << "SuperSystem reference required at " << i << " when number of systems is " << this->superSystems.size();
		throw OutOfBoundsException(ss.str());
	}
	return *this->superSystems[i];
}*/

const SCSystem & SCSystem::GetSuperSystem(const unsigned int i) const {
	if (i >= this->super_systems.size()) {
		stringstream ss; ss << "SuperSystem reference required at " << i << " when number of systems is " << this->super_systems.size();
		throw OutOfBoundsException(ss.str());
	}
	return *this->super_systems[i];
}

/*
SCSystem & SCSystem::SubSystem(const unsigned int i) {
	if (i >= this->subSystems.size()) {
		stringstream ss; ss << "SubSystem reference required at " << i << " when number of systems is " << this->subSystems.size();
		throw OutOfBoundsException(ss.str());
	}
	return *this->subSystems[i];
}*/

const SCSystem & SCSystem::GetSubSystem(const unsigned int i) const {
	if (i >= this->sub_systems.size()) {
		stringstream ss; ss << "SubSystem reference required at " << i << " when number of systems is " << this->sub_systems.size();
		throw OutOfBoundsException(ss.str());
	}
	return *this->sub_systems[i];
}

const bool SCSystem::HasSuperSystem(const SCSystem & sys) const {
	for (vector<SCSystem *>::const_iterator itor = this->super_systems.begin(); itor != this->super_systems.end(); ++itor)
		if(*itor == &sys) return true;
	return false;
}

const bool SCSystem::HasSubSystem(const SCSystem & sys) const {
	for (vector<SCSystem *>::const_iterator itor = this->sub_systems.begin(); itor != this->sub_systems.end(); ++itor)
		if(*itor == &sys) return true;
	return false;
}

const vector<const SCSystem *> SCSystem::ShareWith(const SCSystem & sys) const {
	vector<const SCSystem *> result;
	for (vector<SCSystem *>::const_iterator itor = this->sub_systems.begin(); itor != this->sub_systems.end(); ++itor) {
		for (vector<SCSystem *>::const_iterator itor2 = sys.sub_systems.begin(); itor2 != sys.sub_systems.end(); ++itor2)
			if (*itor == *itor2)
				result.push_back(*itor);
	}
	return result;
}

const bool SCSystem::DoesShareWith(const SCSystem & sys) const {
	for (vector<SCSystem *>::const_iterator itor = this->sub_systems.begin(); itor != this->sub_systems.end(); ++itor) {
		for (vector<SCSystem *>::const_iterator itor2 = sys.sub_systems.begin(); itor2 != sys.sub_systems.end(); ++itor2)
			if (*itor == *itor2)
				return true;
	}
	return false;
}


void SCSystem::addSuperSystem(SCSystem & sys) {
	if (!this->HasSuperSystem(sys))
		this->super_systems.push_back(&sys);
}

void SCSystem::addSubSystem(SCSystem & sys) {
	if (!this->HasSubSystem(sys))
		this->sub_systems.push_back(&sys);
}

void SCSystem::removeSuperSystem(SCSystem & sys) {
	for (vector<SCSystem *>::iterator itor = this->super_systems.begin(); itor != this->super_systems.end(); ++itor) {
		if (*itor == &sys) {
			this->super_systems.erase(itor);
			return;
		}
	}
}

void SCSystem::removeSubSystem(SCSystem & sys) {
	for (vector<SCSystem *>::iterator itor = this->sub_systems.begin(); itor != this->sub_systems.end(); ++itor) {
		if (*itor == &sys) {
			this->sub_systems.erase(itor);
			return;
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
// Closing Namespaces
////////////////////////////////////////////////////////////////////////////////

}

////////////////////////////////////////////////////////////////////////////////
